package com.idroc.storage.support;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.idroc.storage.IoBlock;
import com.idroc.storage.IoBlockFile;
import com.idroc.storage.IoPage;
import com.idroc.storage.IoPageIdUtil;
import com.idroc.storage.IoPageManager;
import com.idroc.storage.StorageOptions;

/**
 * 文件IO分页管理器
 * @author yuyoo (yuyoo4j@163.com)
 * @date 2010-3-30 下午12:59:04
 */
public class FileIoPageManager implements IoPageManager {
	
	/**
	 * logger
	 */
	private static Logger logger = LoggerFactory.getLogger(FileIoPageManager.class);
	 
	/**
	 * 文件IO分页管理器集合
	 */
	private static Map<String, FileIoPageManager> managers = new HashMap<String, FileIoPageManager>();
	
	/**
	 * 存放IO文件的相对根目录路径
	 */
	protected String baseDir = null;
	
	/**
	 * 文件IO分页类型数组
	 */
	protected FileIoPageType[] pageTypes = null;
		
	/**
	 * 构建一个IO分页管理器
	 * @param baseDir
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static FileIoPageManager newInstance(String baseDir) {
		
		FileIoPageManager mgr = managers.get(baseDir);
		if (null == mgr) {
			try {
				// 检查文件目录
				File b = new File(baseDir);
				Map<String, Object> mgrConfig = null;				
				if (b.exists() && b.isDirectory()) {
					logger.debug("baseDir exists");
					ObjectMapper mapper = new ObjectMapper();
					mgrConfig = mapper.readValue(new File(baseDir + "/pageMgr.config"), Map.class); 						
				} else if (b.mkdirs()) {					
					mgrConfig = new HashMap<String, Object>();		
					String path = b.getCanonicalPath();
					path = path.replaceAll("\\\\", "/");
					mgrConfig.put("baseDir", path); // 配置绝对根目录
					
					// 计算IO分页类型与对应的IO分页中IO块的大小;
					int typeCount = 32;
					short[] types = new short[typeCount];
					int[] blockCount = new int[typeCount];
					for (int i = 0; i < typeCount; i++) {
						types[i] = (short) i;
						if (i % 2 == 0) { // 偶数
							if (i == 0) {
								blockCount[i] = 1;
							} else {
								blockCount[i] = blockCount[i - 1] + blockCount[i - 2] ;
							} 
						} else { // 奇数
							if (i == 1) {
								blockCount[i] = 2;
							} else {
								blockCount[i] = blockCount[i - 2] + blockCount[i - 2];
							}
						}
					}
					
					List<Map> pageTypes = new ArrayList<Map>(typeCount);
					for (int i = 0; i < typeCount; i++) {
						try {
							Map<String, Object> type = new HashMap<String, Object>();
							type.put("typeId", String.valueOf(types[i]));
							type.put("blockCount", String.valueOf(blockCount[i]));
							type.put("blockSize", String.valueOf(blockCount[i] * StorageOptions.IO_BLOCK_SIZE));
							String typteBase = "/" + String.format("%04d", types[i]);
							type.put("relativePath", typteBase);
							String cafId = "0";
							type.put("cafId", cafId);
							type.put("rootMetaPageId", String.valueOf(IoPageIdUtil.toPageId((short)types[i], (short)0, 0L)));
							Map<String, String> files = new HashMap<String, String>();
							File tbf = new File(path + typteBase);
							tbf.mkdirs(); 
							String firstFile = typteBase + "/" + cafId + StorageOptions.IO_FILE_EXT;
							files.put(String.valueOf(0), firstFile);
							type.put("files", files);
							pageTypes.add(type);
						} catch (Exception ex) { /* swallow exception */ }
					}
					
					mgrConfig.put("pageTypes", pageTypes);
					
					ObjectMapper mapper = new ObjectMapper();
					mapper.writeValue(new File(baseDir + "/pageMgr.config"), mgrConfig); 
					logger.debug("baseDir not exists");
				} else {
					throw new RuntimeException("无法创建[存放IO文件的相对根目录]");
				}
				mgr = new FileIoPageManager();
				mgr.initialized(mgrConfig);
				managers.put(baseDir, mgr);
			} catch (Exception ex) {
				throw new RuntimeException("构建一个IO分页管理器异常", ex);
			}	
		}
		return mgr;
	} 	
	
	/**
	 * 初始化文件IO分页管理器
	 * @param mgrConfig -- 配置信息
	 */
	private void initialized(Map<String, Object> mgrConfig) {
		
		logger.debug("initialized: -------------> {}", mgrConfig);
		baseDir = (String) mgrConfig.get("baseDir");
		
		List<Map<String, Object>> pts = (List<Map<String, Object>>) mgrConfig.get("pageTypes");
		pageTypes = new FileIoPageType[pts.size()];
		for (int i = 0; i < pts.size(); i++) {
			try {
				Map<String, Object> item = pts.get(i);
				// 获取IO分页类型的参数
				short typeId =  Short.parseShort((String)item.get("typeId"));
				short blockCount = Short.parseShort((String)item.get("blockCount"));
				int blockSize = Integer.parseInt((String)item.get("blockSize"));
				Integer cafId = Integer.valueOf((String)item.get("cafId"));
				Map<String, String> fs = (Map<String, String>) item.get("files");
				// 根据参数构造IO分页类型集合
				Map<Short, IoBlockFile> files = new HashMap<Short, IoBlockFile>();
				for (String fileId : fs.keySet()) {
					Integer fid = Integer.valueOf(fileId);
					String path = baseDir + fs.get(fileId);
					logger.debug("path:{}", path);
					IoBlockFile itemFile = new LocalIoBlockFile(fid.intValue(), path, StorageOptions.IO_BLOCK_SIZE);
					ComposeLocalIoBlockFile ioFile = new ComposeLocalIoBlockFile(itemFile, blockCount);
					files.put(Short.valueOf(fid.shortValue()), ioFile);
				}
				long rootMetaPageId = Long.parseLong((String)item.get("rootMetaPageId"));
				IoBlockFile caf = files.get(cafId.shortValue());				
				pageTypes[i] = new FileIoPageType(typeId, rootMetaPageId, caf, files);
			} catch (Exception ex) {
				ex.printStackTrace();
				throw new RuntimeException("初始化文件IO分页管理器异常", ex);
			}
		}
	}

	public long getFirstPageId(short pageType, int rootNo) {
		// TODO Auto-generated method stub
		return 0;
	}

	public int getFreeRootNo(short pageType) {
		// TODO Auto-generated method stub
		return 0;
	}

	public long getLastPageId(short pageType, int rootNo) {
		// TODO Auto-generated method stub
		return 0;
	}

	public IoPage getRootPage(short pageType, int rootNo) {
		// TODO Auto-generated method stub
		return null;
	}

	public IoPage getUsedPage(long pageId) {
		// TODO Auto-generated method stub
		return null;
	}

	public short maxIoBlockFilesPerType() {
		
		return 0;
	}

	public long maxPagesPerIoBlockFile(short pageType) {
		// TODO Auto-generated method stub
		return 0;
	}

	public long maxSizePerIoBlockFile() {
		// TODO Auto-generated method stub
		return 0;
	}

	public IoPage peekFreePage(short pageType) {
		// TODO Auto-generated method stub
		return null;
	}

	public void releaseUsedPage(long pageId) {
		// TODO Auto-generated method stub
		
	}

	public void releaseUsedPage(short pageType, int rootNo) {
		// TODO Auto-generated method stub
		
	}

	public IoPage takeFreePage(short pageType, int usedRootNo) {
		// TODO Auto-generated method stub
		return null;
	}
		
	/**
	 * IO分页类型
	 * @author yuyoo (yuyoo4j@163.com)
	 * @date 2010-3-30 下午01:57:32
	 */
	private static class FileIoPageType {
		
		/**
		 * IO分页类型ID
		 */
		private short typeId = 0;
		
		/**
		 * 入口元数据的IO分页ID
		 */
		private IoPage rootMetaPage = null;

		/**
		 * 当前分配新空间的IO块文件
		 */
		private IoBlockFile currentAllocateFile = null;
				
		/**
		 * 已经存储IO分页的file集合
		 */
		private Map<Short, IoBlockFile> files = null;
		
		/**
		 * 构造一个IO分页类型
		 * @param typeId -- IO分页类型ID
		 * @param rootMetaPageId -- 入口元数据的IO分页ID
		 * @param currentAllocateFile -- 当前分配新空间的IO块文件
		 * @param files -- 已经存储IO分页的file集合
		 */
		FileIoPageType(short typeId, long rootMetaPageId, IoBlockFile currentAllocateFile, Map<Short, IoBlockFile> files) {
			
			this.typeId = typeId;
			// 构造入口元数据的IO分页
			Short fileId = Short.valueOf(IoPageIdUtil.getFileId(rootMetaPageId));
			long blockIndex = IoPageIdUtil.getIoBlockIndex(rootMetaPageId);
			IoBlockFile file = files.get(fileId);
			logger.debug("blockIndex:{}", blockIndex);
			IoBlock block = file.read(blockIndex * file.getBlockSize());			
			this.rootMetaPage = new FileIoPage(rootMetaPageId, block);
			
			this.currentAllocateFile = currentAllocateFile;
			this.files = files;
		}

		/**
		 * 获取IO分页类型ID
		 * @return -- IO分页类型ID
		 */
		public short getTypeId() {
			
			return typeId;
		}
		
		/**
		 * 获取指定文件ID的IO块文件
		 * @param fileId -- 文件ID
		 * @return -- IO块文件
		 */
		IoBlockFile getIoBlockFile(short fileId) {
			
			return files.get(Short.valueOf(fileId));
		}
		
		/**
		 * 获取已经存储IO分页的file集合
		 * @return -- IO分页的file集合
		 */
		Map<Short, IoBlockFile> getIoBlockFiles() {
			
			return files;
		}
		
		/**
		 * 获取当前分配新空间的IO块文件
		 * @return -- IO块文件
		 */
		IoBlockFile currentAllocateFile() {
			
			return currentAllocateFile;
		}
		
		/**
		 * 获取入口元数据的IO分页
		 * @return -- IO分页
		 */
		IoPage rootMetaPage() {
			
			return rootMetaPage;
		}
		
		@Override
		public String toString() {

			StringBuilder sb = new StringBuilder();
			sb.append("@FileIoPageManager$FileIoPageType{");
			sb.append("typeId:").append(typeId).append(",");
			sb.append("currentAllocateFile:").append(currentAllocateFile).append(",");
			sb.append("files:").append(files);
			sb.append("}");
			return sb.toString();
		}
	}
    
}
